# import the necessary packages
from __future__ import print_function
from PIL import Image
import Send_Alerts as SA
from socket import *
import numpy as np
import argparse
import cv2
import os
import io


# 照片视频格式定义
image_video_types = (".jpg", ".jpeg", ".png", ".bmp", ".tif", ".tiff", ".avi", ".mp4")

#图片视频各自定义
imege_types = (".jpg", ".jpeg", ".png", ".bmp", ".tif")
voide_types = (".avi", ".mp4")


def nms(boxes, probs=None, overlapThresh=0.3):
    # 如果没有框，则返回空列表
    if len(boxes) == 0:
        return []

    # 如果边界框是整数，请将其转换为浮点
    #这很重要，因为我们将进行一系列的划分
    if boxes.dtype.kind == "i":
        boxes = boxes.astype("float")

    # 初始化化的列表
    pick = []

    # 获取边界框的(x,y)坐标
    x1 = boxes[:, 0]
    y1 = boxes[:, 1]
    x2 = boxes[:, 2]
    y2 = boxes[:, 3]

    # 计算边界框的面积并获取索引进行排序
    # (在没有提供概率的情况下，只需按左下角y坐标排序)
    area = (x2 - x1 + 1) * (y2 - y1 + 1)
    idxs = y2

    # 如果提供了概率(存在行人出现)，则按其排序
    if probs is not None:
        idxs = probs

    # 对索引进行排序
    idxs = np.argsort(idxs)

    # keep looping while some indexes still remain in the indexes list
    while len(idxs) > 0:
        # grab the last index in the indexes list and add the index value
        # to the list of picked indexes
        last = len(idxs) - 1
        i = idxs[last]
        pick.append(i)

        # find the largest (x, y) coordinates for the start of the bounding
        # box and the smallest (x, y) coordinates for the end of the bounding
        # box
        xx1 = np.maximum(x1[i], x1[idxs[:last]])
        yy1 = np.maximum(y1[i], y1[idxs[:last]])
        xx2 = np.minimum(x2[i], x2[idxs[:last]])
        yy2 = np.minimum(y2[i], y2[idxs[:last]])

        # compute the width and height of the bounding box
        w = np.maximum(0, xx2 - xx1 + 1)
        h = np.maximum(0, yy2 - yy1 + 1)

        # compute the ratio of overlap
        overlap = (w * h) / area[idxs[:last]]

        # delete all indexes from the index list that have overlap greater
        # than the provided overlap threshold
        idxs = np.delete(idxs, np.concatenate(([last],
                                               np.where(overlap > overlapThresh)[0])))

    # 仅返回拾取的边界框
    return boxes[pick].astype("int")


def list_images(basePath, contains=None):
    # 返回有效的文件集
    return list_files(basePath, validExts=image_video_types, contains=contains)


def list_files(basePath, validExts=None, contains=None):
    valid = True
    # 遍历目录结构
    for (rootDir, dirNames, filenames) in os.walk(basePath):

        # 循环当前目录中的文件名
        for filename in filenames:

            # 遍历目录结构
            if contains is not None and filename.find(contains) == -1:
                continue

            # 确定当前文件的文件扩展名
            ext = filename[filename.rfind("."):].lower()

            # 检查该文件是否为图像或者视频，是否应进行处理
            if validExts is None or ext.endswith(validExts):
                # 构建图像的路径并生成它
                imagePath = os.path.join(rootDir, filename)
                if(validExts) is  None or ext.endswith(imege_types):
                    valid = True
                    yield imagePath, valid

                if (validExts) is None or ext.endswith(voide_types):
                    valid = False


#用于计算照片高度和宽度
def resize(image, width=None, height=None, inter=cv2.INTER_AREA):
    dim = None
    (h, w) = image.shape[:2]
    # 如果高和宽为None则直接返回
    if width is None and height is None:
        return image
    # 检查宽是否是None
    if width is None:
        # 计算高度的比例并并按照比例计算宽度
        r = height / float(h)
        dim = (int(w * r), height)
    # 高为None
    else:
        # 计算宽度比例，并计算高度
        r = width / float(w)
        dim = (width, int(h * r))
    resized = cv2.resize(image, dim, interpolation=inter)
    # 返回调整大小的图像
    return resized

#用于图片上的行人检测
def photo_detection(hog,orig,image,imagePath):
    # 检测图像中的行人
    (rects, weights) = hog.detectMultiScale(image, winStride=(4, 4),
                                            padding=(8, 8), scale=1.03)  # 通过人脸来检测
    # 绘制原始边界框
    #print(rects)

    #用于返回判断结果
    if len(rects) == 0:
        print("No pedestrians!!!")

    for (x, y, w, h) in rects:
        cv2.rectangle(orig, (x, y), (x + w, y + h), (255, 0, 0), 2)

    # 使用相当大的重叠阈值对边界框应用非极大值抑制，以尝试保持仍然是人的重叠框
    rects = np.array([[x, y, x + w, y + h] for (x, y, w, h) in rects])
    pick = nms(rects, probs=None, overlapThresh=0.35)

    # 绘制最终边界框
    for (xA, yA, xB, yB) in pick:
        cv2.rectangle(image, (xA, yA), (xB, yB), (0, 255, 0), 2)

    # 显示有关边界框数量的一些信息
    filename = imagePath[imagePath.rfind("/") + 1:]
    print("[INFO] {}: {} original boxes, {} after suppression".format(
        filename, len(rects), len(pick)))

    # 显示输出图像
    cv2.imshow("Before NMS", orig)
    cv2.imshow("After NMS", image)

    cv2.waitKey(0)
    

#用于视频上行人检测
def video_detection(hog,orig,video):

    (rects, weights) = hog.detectMultiScale(video, winStride=(4, 4),
                                         padding=(8, 8), scale=1.03)  # 通过人脸来检测

    for (x, y, w, h) in rects:
        cv2.rectangle(orig, (x, y), (x + w, y + h), (255, 0, 0), 2)


    # 使用相当大的重叠阈值对边界框应用非极大值抑制，以尝试保持仍然是人的重叠框
    rects = np.array([[x, y, x + w, y + h] for (x, y, w, h) in rects])
    pick = nms(rects, probs=None, overlapThresh=0.35)

    # 绘制最终边界框
    for (xA, yA, xB, yB) in pick:
        cv2.rectangle(video, (xA, yA), (xB, yB), (0, 255, 0), 2)

    cv2.imshow("Before NMS", orig)
    cv2.imshow("After NMS", video)

    # 在识别画框后发出指令
    if len(rects) == 0:
        # print(len(rects))
        SA.send_alerts("0")
    else:
        SA.send_alerts('1')

    cv2.waitKey(1)

#用于文件夹寻找图片和视频
#判断图片、视频，选择检测方案以及初始化HOG、SWM，构建参数解析器
# def pede_dete():
#
#     # 构造参数解析器并解析参数,用于寻找文件夹下图片
#     ap = argparse.ArgumentParser()
#     ap.add_argument("-iv", "--image_video", default='test1', help="path to images directory")
#     args = vars(ap.parse_args())
#
#     # 初始化 HOG 描述符/人物检测器
#     hog = cv2.HOGDescriptor()
#
#     #设置SWM模型
#     hog.setSVMDetector()cv2.HOGDescriptor_getDefaultPeopleDetector()
#
#     # 在图像路径上循环,寻找图片
#     for imagePath, valid in list_images(args["image_video"]):
#         # 加载图像和视频并调整其大小以
#         # （1）减少检测时间
#         # （2）提高检测精度
#         if valid is True:
#             image = cv2.imread(imagePath)
#             image = resize(image, width=min(400, image.shape[1]))     #压缩照片宽度
#             orig = image.copy()         #复制照片，防止拍摄的照片被改变
#             image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
#             orig = cv2.cvtColor(orig, cv2.COLOR_BGR2GRAY)
#             #print(image)         #打印照片像素点值
#             photo_detection(hog, orig, image, imagePath)
#
#         if valid is False:
#             vc = cv2.VideoCapture(imagePath)
#             # 检查是否打开正确
#             if vc.isOpened():
#                 oepn, frame = vc.read()
#             else:
#                 oepn = False
#             #视频转换成图片用于识别
#             while oepn:
#                 ret, frame = vc.read()
#                 if frame is None:
#                     break
#                 if ret == True:
#                    video_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
#                    video = resize(video_gray, width=min(400, video_gray.shape[1]))   # 压缩视频宽度
#                   #video = cv2.GaussianBlur(video, (21, 21), 0)
#                    orig = video.copy()
#                    video_detection(hog, orig, video)


#通过局域网接受视频
def server_videos():
    image_videos = socket(AF_INET, SOCK_DGRAM, 0)
    addr = ('0.0.0.0', 9090)
    image_videos.bind(addr)
    while True:
        # 初始化 HOG 描述符/人物检测器
        hog_ser = cv2.HOGDescriptor()
        # 设置SWM模型
        hog_ser.setSVMDetector(cv2.HOGDescriptor_getDefaultPeopleDetector())

        data, _ = image_videos.recvfrom(921600)  #接受视频数据
        receive_data = np.frombuffer(data, dtype='uint8') #视频数组，转化为uint8类型
        video = cv2.imdecode(receive_data, 1)     #对转化后的数组进行解码
        video = cv2.cvtColor(video, cv2.COLOR_RGB2BGR)  # ESP32采集的是RGB格式，要转换为BGR（opencv的格式）

        #video = video.reshape(480, 640, 3)
       #cv2.putText(video, "server", (50, 50), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 0, 0), 2)
        video = cv2.cvtColor(video, cv2.COLOR_BGR2GRAY) #将图片灰度化
        video_gray = resize(video, width=min(400, video.shape[1]))  # 压缩视频宽度
        #video = cv2.GaussianBlur(video, (21, 21), 0) #高斯滤波器用于模糊化
        orig = video_gray.copy()  #深度复制图片
        #orig_gray = cv2.cvtColor(orig, cv2.COLOR_BGR2GRAY) #将图片灰度化
        #orig_gray = resize(orig_gray, width=min(400, orig_gray.shape[1]))  # 压缩视频宽度

        video_detection(hog_ser, orig, video_gray) #传入图片用于行人检测


if __name__=='__main__':
  server_videos()
  # pede_dete()